package main

import (
	"fmt"
	"jvmgo/instructions"
	"jvmgo/instructions/base"
	"jvmgo/rtda"
	"jvmgo/rtda/heap"
)

func interpreter(thread *rtda.Thread, logInst bool) {
	//codeAttr := methodInfo.CodeAttribute()
	//maxLocals := codeAttr.MaxLocals()
	//maxStack := codeAttr.MaxStack()
	//bytecode := codeAttr.Code()
	defer catchErr(thread)
	loop(thread, logInst)
}

func createArgsArray(loader *heap.ClassLoader, args []string) *heap.Object {
	stringClass := loader.LoadClass("java/lang/String")
	argsArr := stringClass.ArrayClass().NewArray(uint(len(args)))
	jArgs := argsArr.Refs()
	for i, arg := range args {
		jArgs[i] = heap.JString(loader, arg)
	}
	return argsArr
}

func catchErr(thread *rtda.Thread) {
	if r := recover(); r != nil {
		logFrames(thread)
		panic(r)
	}
}

func logFrames(thread *rtda.Thread) {
	for !thread.IsStackEmpty() {
		frame := thread.PopFrame()
		method := frame.Method()
		className := method.Class().Name()
		fmt.Printf(">> pc:%4d %v.%v%v \n", frame.NextPC(), className, method.Name(), method.Descriptor())
	}

}

func loop(thread *rtda.Thread, logInst bool) {
	reader := &base.BytecodeReader{}
	for true {
		frame := thread.CurrentFrame()
		pc := frame.NextPC()
		thread.SetPC(pc)
		// decode
		reader.Reset(frame.Method().Code(), pc)
		opcode := reader.ReadUint8()
		inst := instructions.NewInstruction(opcode)
		inst.FetchOperands(reader)
		frame.SetNextPC(reader.PC())
		if !logInst {
			logInstruction(frame, inst)
		}
		// execute
		//fmt.Printf("pc %2d inst:%T %v\n", pc, inst, inst)

		inst.Execute(frame)
		if thread.IsStackEmpty() {
			break
		}
	}
}

func logInstruction(frame *rtda.Frame, inst base.Instruction) {
	method := frame.Method()
	className := method.Class().Name()
	methodName := method.Name()
	pc := frame.Thread().PC()
	if className == "sun/nio/cs/FastCharsetProvider" && methodName == "canonicalize" {
		fmt.Println("canonicalize")
	}
	if className == "java/lang/Class" && methodName == "arrayContentsEq" {
		fmt.Println("arrayContentsEq")
	}
	if className == "java/lang/reflect/Constructor" && methodName == "getParameterTypes" {
		fmt.Println("getParameterTypes")
	}
	if className == "java/lang/ClassLoader" && methodName == "loadLibrary" {
		if pc == 220 {
			fmt.Println("220")
		}
	}
	if className == "java/lang/ClassLoader" && methodName == "loadLibrary" {

	}
	if className == "ThrowableTest" && methodName == "main" && pc == 3 {
		fmt.Println("ThrowableTest.main() # 3 *math.IDIV &{{}}\n")
	}
	//if className == "java/lang/ClassLoader"
	fmt.Printf("%v.%v() #%2d %T %v\n", className, methodName, pc, inst, inst)
}
